import { defineComponent, ref, watch } from 'vue';
import { isEqual, cloneDeep } from 'lodash-es';
import PropTypes from '../types';
import { withInstall } from '@/components/utils';
import AddConditionButton from './AddConditionButton';
import ConditionGroupItem from './ConditionGroupItem';
import './style/index.less';

interface ConditionInfo {
  key?: number;
  operator?: string;
  children?: ConditionInfo[];
}

const ConditionGroupProps = {
  // 空的表达式数据
  expression: PropTypes.object.def({
    filterType: 'CONDITION',
    filterCondition: {
      filterColumnName: '',
      type: '',
      conditionType: '',
      dateFormat: 'yyyy-MM-dd',
      value: null,
    },
  }),
  // 数据
  modelValue: PropTypes.object,
};
const ConditionGroup = defineComponent({
  name: 'ConditionGroup',
  props: ConditionGroupProps,
  emits: ['update:modelValue'],
  setup(props, { emit, slots, expose }) {
    // ------------------------------- 属性
    const key = ref(0);
    // 数据源
    const conditionData = ref<ConditionInfo | null>({});

    // 当前选中
    const selectedCondition = ref<ConditionInfo | null>(null);
    // 当前选中父级
    const selectedParentCondition = ref<ConditionInfo | null>(null);
    // ------------------------------- 元素节点
    const buttonDefaultSolts = slots.buttonGroup
      ? slots.buttonGroup
      : () => (
          <div class="condition-button-group">
            <AddConditionButton
              buttonType="条件"
              class="condition-button"
              onClick={handleAddCondition}
            />
            <AddConditionButton
              buttonType="公式"
              class="condition-button"
              onClick={handleAddFormula}
            />
          </div>
        );

    // ------------------------------ 监听
    watch(() => props.modelValue, dealDataSource, {
      deep: true,
      immediate: true,
    });

    // ----------------------------- 交互
    // 条件表达式发生变化时，向组件外抛出事件
    function handleConditionDataChange() {
      emit('update:modelValue', conditionData.value);
    }
    /**
     * 添加表达式节点的方法
     * 注：自定义操作按钮时，可以在组件外调用该方法添加新的表达式节点
     * @param {String} operator 逻辑运算符的枚举值：AND/OR
     * @param {Object} customExpression 空表达式节点的对象，该参数缺省时使用props传进来的expression属性
     */
    function handleAddCondition(operator, customExpression) {
      const newExpressionNode: ConditionInfo = generateExpressionNode(
        customExpression || props.expression,
      );
      // 如果根节点改没有初始化，则初始化根节点
      const { children } = conditionData.value || {};
      if (!children || !children.length) {
        conditionData.value = {
          operator,
          key: key.value++,
          children: [newExpressionNode],
        };
        handleConditionDataChange();
        return;
      }

      // 如果当前节点不存在父节点，则当前节点为根节点
      if (!selectedParentCondition.value || !Object.keys(selectedParentCondition.value).length) {
        /*
         * 如果新添加的逻辑运算符与跟节点的一致，则直接将新增的表达式节点增加到跟节点的子表达式列表中
         * 否则将新增的表达式节点和根节点，使用新添加的逻辑运算符合并成一个表达式作为跟节点
         */
        if (conditionData.value?.operator === operator) {
          conditionData.value?.children?.push(newExpressionNode);
        } else {
          conditionData.value = mergeCondition(conditionData.value, newExpressionNode, operator);
        }
      } else {
        // 获取当前表达式在父节点表达式列表中的下标
        const index = selectedParentCondition.value?.children?.findIndex(
          (item) => item === selectedCondition.value,
        );
        /*
         * 如果新添加的逻辑运算符与父节点的一致，则给父节点的子表达式列表中、当前表达式的后面插入新的表达式节点
         * 否则将新增的表达式节点和当前节点的表达式，使用新添加的逻辑运算符合并成一个表达式，并替换当前节点的表达式
         */
        if (selectedParentCondition.value.operator === operator) {
          selectedParentCondition.value?.children?.splice(
            (index as number) + 1,
            0,
            newExpressionNode,
          );
        } else {
          const newCondition = mergeCondition(selectedCondition.value, newExpressionNode, operator);
          selectedParentCondition.value?.children?.splice(index as number, 1, newCondition);
        }
      }
      // 清空选中的节点
      selectedCondition.value = null;
      selectedParentCondition.value = null;

      handleConditionDataChange();
    }
    // 公式点击事件
    function handleAddFormula(_operator) {}
    // 生成一个表达式节点（条件表达式树的叶子节点）
    function generateExpressionNode(expression) {
      return {
        expression: cloneDeep(expression),
        key: key.value++,
      };
    }
    // 将两个表达式节点合并成一个节点
    function mergeCondition(condition1, condition2, operator): ConditionInfo {
      return {
        operator,
        key: key.value++,
        children: [condition1, condition2],
      };
    }
    // 数据源改变通知
    function dealDataSource() {
      // 如果value的值与conditionData的值一致，则不需要初始化
      if (isEqual(conditionData.value, props.modelValue)) {
        return;
      }

      // 如果value的值不是一个树状数据，则清空条件数据
      if (!props.modelValue || !props.modelValue.children) {
        conditionData.value = {};
        return;
      }

      // 初始化传入的数据
      conditionData.value = initTreeNodeKey(cloneDeep(props.modelValue));
      // 更新完conditionData后，抛出数据变化的事件，保证组件内外的conditionData使用同一个对象，以便可以通过作用域插槽修改数据
      handleConditionDataChange();
    }

    /**
     * 选中一个条件表达式节点，
     * 注：自定义选中表达式节点的方式时，可以在组件外调用该方法
     * @param {Object} currentCondition 当前表达式的树节点
     * @param {Object} parentCondition 当前表达式节点的父节点
     */
    function handleSelectCondition(currentCondition, parentCondition) {
      // 如果当前选择的节点与之前选中的节点一样，则清除选中的节点；否则缓存当前选择的节点
      if (selectedCondition.value === currentCondition) {
        selectedCondition.value = null;
        selectedParentCondition.value = null;
      } else {
        selectedCondition.value = currentCondition;
        selectedParentCondition.value = parentCondition;
      }
    }

    // 删除条件表达式节点
    function handleDeleteCondition(currentCondition, parentCondition) {
      // 删除当前节点
      const deleteIndex = parentCondition.children.findIndex((item) => item === currentCondition);
      parentCondition.children.splice(deleteIndex, 1);

      // 如果删除当前节点后，其父节点没有了子节点，则清理一次树中的空节点
      if (parentCondition.children.length === 0) {
        conditionData.value = trimConditionTree(conditionData.value);
      }

      // 清空已选中的节点
      selectedCondition.value = null;
      selectedParentCondition.value = null;

      handleConditionDataChange();
    }

    // 处理条件格式插槽
    function handleDealExpressionSlots(soltsData) {
      return slots.expressionSolt && slots.expressionSolt(soltsData);
    }
    // 删除表达式树中的空节点
    function trimConditionTree(conditionTree) {
      const { expression, children } = conditionTree;
      // 如果是叶子节点，则直接返回
      if (expression || !children) {
        return conditionTree;
      }

      // 递归删除子节点中的空子节点
      children.forEach((treeNode) => {
        trimConditionTree(treeNode);
      });

      // 过滤当前节点的子节点，只保留叶子节点及子节点不是空数组的节点
      conditionTree.children = children.filter(
        (treeNode) => treeNode.expression || (treeNode.children && treeNode.children.length),
      );

      return conditionTree;
    }
    // 初始化树节点的key
    function initTreeNodeKey(treeData) {
      treeData.key = key.value++;
      // 如果当前节点存在子节点，则给所有子节点初始化key
      if (treeData.children && treeData.children.length) {
        treeData.children.forEach((treeNode) => {
          initTreeNodeKey(treeNode);
        });
      }
      return treeData;
    }

    // 暴露给组件外，可供父组件使用ref调用的方法
    expose({
      handleAddCondition,
      handleSelectCondition,
    });

    return () => (
      <div class="condition-group-wrapper">
        {/*按钮部分*/}
        {buttonDefaultSolts()}
        {/*条件表达式组 */}
        {(conditionData.value?.children?.length as number) > 0 && (
          <ConditionGroupItem
            class="condition-group"
            condition-data={conditionData.value}
            selected-condition={selectedCondition.value}
            onSelectCondition={handleSelectCondition}
            onDeleteCondition={handleDeleteCondition}
            v-slots={{ expressionSolt: (props) => handleDealExpressionSlots(props) }}
          />
        )}
      </div>
    );
  },
});

export default withInstall(ConditionGroup);
